Acknowledgements

This material is adapted from an R for RNA-Seq workshop originally run here.

R for RNA-Seq course

This course has been designed to introduce biologists to R for RNA-Seq analysis. The focus here is on using tidyverse to analyse RNA-Seq data, as we believe this is a productive and engaging way to learn R for RNA-Seq analysis. In this course we will use some new packages, ttBulk and tidyHeatmap. These packages provide a friendly tidyverse-style way to perform analysis of RNA-Seq data.

Setup

Data files

Data files are available from the data folder in GitHub here. You should download the files listed below and place them into a folder called data in your working directory.

Data files:

  • GSE60450_GeneLevel_Raw_data.csv
  • GSE60450_filtered_metadata.csv

R Packages

Packages used:

  • tidyverse
  • ttBulk
  • tidyHeatmap
  • edgeR
  • devtools

Here we will use packages from the 3 main repositories of R packages: Bioconductor, CRAN and GitHub. To install the packages you can follow the steps below.

R version

Bioconductor packages

  • Get the latest version of Bioconductor and edgeR package by starting R and entering the commands:
if (!requireNamespace("BiocManager"))
    install.packages("BiocManager")
BiocManager::install("edgeR")

CRAN packages

Install the CRAN packages with the command below.

install.packages(c("tidyverse", "devtools"))

GitHub packages

ttBulk will be added to Bioconductor and when it is added there you can install it using the usual Bioconductor commands. tidyHeatmap will be added to CRAN and when it is there you can install it with the install.packages() command. In the meantime you can install ttBulk and tidyHeatmap from their development sites in Github with the commands below.

# install ttBulk
devtools::install_github("stemangiola/ttBulk@dev")

# install tidyHeatmap
devtools::install_github("stemangiola/tidyHeatmap")

Overview

TODO: Maybe add workflow image showing steps and functions used

  • Reading in tables of counts and metadata
  • Filtering and Normalisation of counts
  • Differential expression analysis
  • Visualisation (Heatmaps, MA plot, Volcano plot)

Introduction and data import

Measuring gene expression on a genome-wide scale has become common practice over the last two decades or so, with microarrays predominantly used pre-2008. With the advent of next generation sequencing technology in 2008, an increasing number of scientists use this technology to measure and understand changes in gene expression in often complex systems. As sequencing costs have decreased, using RNA-Seq to simultaneously measure the expression of tens of thousands of genes for multiple samples has never been easier. The cost of these experiments has now moved from generating the data to storing and analysing it.

There are many steps involved in analysing an RNA-Seq experiment. Analysing an RNAseq experiment begins with sequencing reads. These are aligned to a reference genome, then the number of reads mapped to each gene can be counted. This results in a table of counts, which is what we perform statistical analyses on in R. While mapping and counting are important and necessary tasks, today we will be starting from the count data and getting stuck into analysis.

First, let’s load all the packages we will need to analyse the data.

# load libraries
library(tidyverse)
library(ttBulk)
library(tidyHeatmap)

GREIN repository of RNA-Seq datasets

In this tutorial, we will learn some R through creating plots to visualise data from an RNA-Seq experiment. RNA-Seq counts file can be obtained from the GREIN platform. GREIN stands for GEO RNA-Seq Experiments Interactive Navigator and provides >6,500 published datasets from GEO that have been uniformly processed. It is available at http://www.ilincs.org/apps/grein/. You can search for a dataset of interest using the GEO code. We obtained the dataset used here using the code GSE60450. GREIN provide QC metrics for the RNA-Seq datasets and both raw and normalised counts. We will use the raw counts here. Generally, the higher the number of counts the more the gene is expressed.

Mouse mammary gland dataset

Here we will perform RNA-Seq analysis using data from a breast cancer research study, from the paper by Fu et al. 2015, GEO code GSE60450. This study examined gene expression in basal and luminal cells from mice at different stages of mammary gland development (virgin, pregnant and lactating). There are 2 samples per group and 6 groups, 12 samples in total.

Reading in the data

Set up an RStudio project specifying the directory where you have saved the /data directory. Open a new script for this workshop File > New File > R Script. Save it as e.g. intro-rnaseq.R.

# read in counts file
counts <- read_csv("data/GSE60450_GeneLevel_Raw_data.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_character(),
  gene_symbol = col_character(),
  GSM1480291 = col_double(),
  GSM1480292 = col_double(),
  GSM1480293 = col_double(),
  GSM1480294 = col_double(),
  GSM1480295 = col_double(),
  GSM1480296 = col_double(),
  GSM1480297 = col_double(),
  GSM1480298 = col_double(),
  GSM1480299 = col_double(),
  GSM1480300 = col_double(),
  GSM1480301 = col_double(),
  GSM1480302 = col_double()
)
# read in metadata
sampleinfo <- read_csv("data/GSE60450_filtered_metadata.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_character(),
  characteristics = col_character(),
  immunophenotype = col_character(),
  `developmental stage` = col_character()
)

Let’s take a look at the data. You can type the name of the object to view the first few lines and to see how many rows and columns it has.

counts

The counts object contains information about genes (one gene per row), the first column has the Ensembl gene id, the second has the gene symbol and the remaining columns contain information about the number of reads aligning to the gene in each experimental sample. Note the gene counts here are not integers as they’re estimated counts from salmon (see here: https://support.bioconductor.org/p/101156/). There are two replicates for each cell type and time point (detailed sample info can be found in file “GSE60450_series_matrix.txt” from the GEO website). The sampleinfo metadata file contains basic information about the samples that we will need for the analysis today.

First we will convert the counts into long format (tidy format), similar to what we did in the Intro to R session.

# convert to tidy format
counts <- pivot_longer(counts, cols = starts_with("GSM"), names_to = "sample", values_to = "count") 
# take a look
counts

We will next extract just the columns we need, sample, gene_symbol, count. To do this we will use the tidyverse pipe %>%. This ‘pipes’ the output from the command on the left into the command on the right/below. Using the pipe is not essential but it reduces the amount of code we need to write when we have multiple steps (as we’ll see later). It also can make the steps clearer and easier to see. For more details on the pipe see here.

# using pipe
counts <- counts %>% 
  select(sample, gene_symbol, count, X1)
# take a look 
counts

Take a look at the sampleinfo file. The first column “X1” contains the sample ids, the second “characteristics” contains the specific group the sample belongs to (e.g. mammary gland, luminal cells, virgin), the third column “immunophenotype” contains just the cell type (luminal or basal) and the fourth column “developmental stage” contains just the stage (virgin, pregnant or lactating).

sampleinfo

We want to compare the groups in the “characteristics” column however the names are quite long so, similar to what we did in the Intro to R session, we’ll make a column containing shorter group names.

# make column called condition with shorter group names
sampleinfo <- mutate(sampleinfo, condition = case_when(                     str_detect(characteristics, "basal.*virgin") ~  "bvirg",
        str_detect(characteristics, "basal.*preg")  ~  "bpreg",
        str_detect(characteristics, "basal.*lact")  ~  "blact",
        str_detect(characteristics, "luminal.*virgin")  ~  "lvirg",
        str_detect(characteristics, "luminal.*preg")  ~  "lpreg",
        str_detect(characteristics, "luminal.*lact")  ~  "llact"
       ))
sampleinfo

Now we have our counts matrix in the long format we will join it to our sampleinfo so we have information on the samples, what groups they belong to. This is similar to what we did in the Intro to R session.

counts <- full_join(counts, sampleinfo, by = c("sample" = "X1"))
# take a look
counts

Now that we have our data in the format we want we will create a ttBulk object, that we can use to perform differential expression analysis with the ttBulk package. For this we need to specify our counts object and the names of the columns that contain our sample ids, our gene identifiers and our counts. Any other columns in the counts object e.g. our Ensembl gene id “X1” column will remain at the end.

#create a 'tt' object
counts <- ttBulk(counts, sample, gene_symbol, count)
# take a look
counts

Some gene symbols are not unique, they map to more than one gene id. We need to remove this redundancy and we can do that with ttBulk function aggregate_duplicates(). By default it will aggregate duplicate gene symbols summing their counts. Add way to identify what the duplicates are?

# get rid of duplicated gene symbols
counts <- aggregate_duplicates(counts)

Filtering and Normalisation

Before testing for differential expression we need to do some preprocessing of the data. We filter lowly expressed genes and also normalise for differences in sequencing depth and composition between the samples. These steps are explained in more details below.

First we will check how many counts we have for each sample. We can do this quite easily by making a bar plot. This helps us see whether there are any major discrepancies between the samples more easily.

# make barplot of counts
ggplot(data=counts, mapping=aes(x=sample, weight=count, fill=condition)) + 
  geom_bar()

The bar plots show us there are ~20 million counts per sample.

Filtering lowly expressed genes

Genes with very low counts across all libraries provide little evidence for differential expression and they interfere with some of the statistical approximations that are used later in the pipeline. They also add to the multiple testing burden when estimating false discovery rates, reducing power to detect differentially expressed genes. These genes should be filtered out prior to further analysis.

There are a few ways to filter out lowly expressed genes. When there are biological replicates in each group, we favour filtering on a minimum count threshold in the smallest group size. This ensures that a gene will be retained if it is only expressed in one group. In this dataset, we choose to retain genes if they are expressed at a counts-per-million (CPM) above 0.5 in at least two samples (our smallest group size). As a general rule, a good CPM threshold can be chosen by identifying the CPM that corresponds to a count of 10, which in this case is about 0.5 as there are ~20 million counts per sample (20/10 = 0.5 CPM). If the count is any smaller, it is considered to be very low, indicating that the associated gene is not expressed in that sample. Smaller CPM thresholds are usually appropriate for larger libraries. So for this dataset we will filter lowly expressed genes using a cpm_threshold of 0.5 and a prop_threshold (proportion of samples) of 2/12 with ttBulk.

Normalisation for sequencing depth and composition

TMM normalisation is performed to eliminate composition biases between libraries [@robinson2010tmm]. This generates a set of normalisation factors, where the product of these factors and the library sizes defines the effective library size. TMM normalisation (and most scaling normalisation methods) scale relative to one sample.

In the ttBulk package the function scale_abundance() performs the filtering and normalisation to generate normalised counts.

# Normalisation for library size and composition bias (scale counts), 
counts.norm <- counts %>% scale_abundance(
  cpm_threshold = 0.5,
  prop_threshold = 2/12)
# take a look
counts.norm

After we run scale_abundance() we should see some columns have been added to the end of counts. We have a column called filter out low counts that indicates whether the gene has been filtered due to being lowly expressed, FALSE means it wasn’t filtered, TRUE means it was. The count scaled column contains the scaled counts, after the normalisation has been applied.

Maybe add a way to see how many genes we have for each sample, before and after filtering?

We can create density plots to view the distributions of the counts for the samples before and after filtering. This is also a quality check to see if the samples look similar and that none look majorly different. Note we need to take the log2 of the counts and add a small offset (1) to avoid taking log of zero.

# density plot before 
counts.norm %>% 
    ggplot(aes(x=log2(`count scaled` + 1), group=sample, color=condition)) +
    geom_density()

The large peak on the left of the plot shows that a large proportion of genes within each sample are not expressed or lowly-expressed.

Exercise

Adapt the code above to create a density plot of the counts after filtering lowly expressed genes. How does it compare it to the density plot above?

# Solution
counts.norm %>% 
  filter(`filter out low counts` == FALSE) %>%
    ggplot(aes(x=log2(`count scaled`+1), group=sample, color=condition)) +
    geom_density()

We can also create box plots to check the distributions of the counts in the samples. We can add a line through the median to help us see how similar (or not) the distributions are.

# box plot before scaling
counts.norm %>% 
  filter(`filter out low counts` == FALSE) %>%
    ggplot(aes(x=sample, y=log2(count+1), fill=condition)) +
    geom_boxplot() +
  geom_hline(aes(yintercept = median(log2(count+1)), colour = 'red'))

Exercise

Adapt the code above to create box plots that only includes the genes that have not been filtered due to low counts. How does it compare it to the box plots above?

# box plot after filtering
counts.norm %>% 
  filter(`filter out low counts` == FALSE) %>%
    ggplot(aes(x=sample, y=log2(`count scaled`+1), fill=condition)) +
    geom_boxplot() +
  geom_hline(aes(yintercept = median(log2(`count scaled`+1)), colour = 'red'))

Multidimensional scaling plots

By far, one of the most important plots we make when we analyse RNA-Seq data are MDS plots. An MDS plot is a visualisation of a principal components analysis, which determines the greatest sources of variation in the data. A principal components analysis is an example of an unsupervised analysis, where we don’t need to specify the groups. If your experiment is well controlled and has worked well, what we hope to see is that the greatest sources of variation in the data are the treatments/groups we are interested in. It is also an incredibly useful tool for quality control and checking for outliers. We can use the reduce_dimensions() function to calculate the dimensions.

# get MDS dimensions
counts.norm.MDS <-
  counts.norm %>%
  reduce_dimensions(method="MDS", .dims = 2)
# take a look
counts.norm.MDS

Then we can select just the dimensions for the samples.

# get the dimensions with all metadata
MDSdims <- counts.norm.MDS %>%
select(contains("Dim"), sample, immunophenotype, `developmental stage`, condition) %>%
distinct()
# take a look
MDSdims

Next we can plot the MDS dimensions as a scatterplot.

# MDS plot
ggplot(MDSdims, aes(x=Dim1, y=Dim2, colour=sample)) + 
  geom_point()

Exercise

Colour the MDS plot with different metadata variables e.g. immunophenotype. Try using shape= inside the aes(). You can use ?geom_point to check the help page. Discuss what is the greatest source of variation in the data (i.e. what does dimension 1 represent)? What is the second greatest source of variation in the data?

Solution

# MDS plot coloured by cell type
ggplot(MDSdims, aes(x=Dim1, y=Dim2, colour=condition)) + 
  geom_point()

# MDS plot coloured by cell type
ggplot(MDSdims, aes(x=Dim1, y=Dim2, colour=immunophenotype)) + 
  geom_point()

# MDS plot coloured by stage
ggplot(MDSdims, aes(x=Dim1, y=Dim2, colour=`developmental stage`)) + 
  geom_point()

# MDS plot coloured by stage with shape for cell type
ggplot(MDSdims, aes(x=Dim1, y=Dim2, colour=`developmental stage`, shape=immunophenotype)) + 
  geom_point()

Demo more MDS plots (sample swap, batch effects)

Differential expression

Now that we are happy that the data looks good, we can continue to testing for differentially expressed genes. We will use the test_differential_abundance() from ttBulk which uses edgeR to perform the differential expression analysis. We give test_differential_abundance() our ttBulk counts object, our filtering thresholds and a formula (e.g. 0 + condition), specifying the column that contains our groups to be compared. We can also provide the names of the groups we want to compare/contrast to .contrasts (e.g. .contrasts = c(“conditionbpreg - conditionblact”))

# Differential expression with limma-voom (or edgeR)
counts.de <- counts %>%
    test_differential_abundance(
      cpm_threshold = 0.5,
      prop_threshold = 2/12,
      ~ 0 + condition,
      .contrasts = c("conditionbpreg - conditionblact"))
ttBulk says: The design column names are "conditionblact, conditionbpreg, conditionbvirg, conditionllact, conditionlpreg, conditionlvirg" in case you are interested in contrasts
Joining, by = "gene_symbol"
Joining, by = "gene_symbol"
# take a look
counts.de

Now we have columns with our logFC and FDR P values.

Exercise

Perform differential expression for lpreg vs llact

Perform for 2 contrasts, bpreg vs blact and lpreg vs llact at the same time

# maybe include
# remove condition from column names
# counts.de <- counts.de %>% 
#  rename_at(vars(contains("condition")), ~ str_replace(., "_condition", ""))

We can take a look at the top genes by P value. We can use filter() to select only genes with FDR < 0.05, then use distinct() to get the unique values for columns of interest, and arrange() to sort the table by PValue.

counts.de %>%
  filter(FDR < 0.05) %>%
  distinct(gene_symbol, logFC, PValue, FDR) %>%
  arrange(PValue)

We can write out our results to a file that can be loaded into e.g. Excel. write_tsv() will create a tab-separated file.

# save results
write_tsv(counts.de, "my_de_results.tsv")

Plots after testing for DE

Let’s make a few plots to make sure everything looks good and that we haven’t made a mistake in the analysis. Genome-wide plots that are useful for checking are MA plots and volcano plots. We can also use stripcharts and heatmaps to visualise groups of genes.

MA plots

MA plots enable us to visualise amount of expression (logCPM) versus logFC. Highly expressed genes are towards the right of the plot. We can also colour significant genes (e.g. genes with FDR < 0.05)

# maplot, minimal
counts.de %>%
  filter(`filter out low counts` == FALSE) %>%
  ggplot(aes(x=logCPM, y=-logFC, colour=is_de)) +
  geom_point()

# colour by logfc & set up to red, down to blue

Volcano plots

Volcano plots enable us to visualise significance of expression (logCPM) versus logFC. Highly significant genes are towards the top of the plot. We can also colour significant genes (e.g. genes with FDR < 0.05)

# volcanoplot, minimal
counts.de %>%
  filter(`filter out low counts` == FALSE) %>%
  ggplot(aes(x=logFC, y=-log10(PValue), colour=is_de)) +
  geom_point()

# colour by logfc & set up to red, down to blue, highlight some genes

To see how to make more complicated volcano plots, including how to label genes in the plot, see the volcano plot tutorial here. More complicated MA plots could also be made in a similar way.

Stripcharts

In addition to the genome-wide plots already discussed, it is recommended to have a look at the expression levels of the individual samples for the genes of interest, before following up on the DE genes with further lab work. We can use stripcharts and heatmaps to do this. These will help show if expression is consistent amongst replicates in the groups.

For these plots showing expression in the individual samples, we need the normalised counts per gene per sample, so we will first join the scaled counts to the de results.

counts.de <- full_join(counts.de, counts.norm)
Joining, by = c("sample", "gene_symbol", "count", "X1", "characteristics", "immunophenotype", "developmental stage", "condition", "merged transcripts", "filter out low counts")

With stripcharts we can see if replicates tend to group together and how the expression compares to the other groups. Note this is bit more complicated maybe omit. Or break it into steps e.g. get top genes, then plot

# stripcharts
counts.de %>%
    inner_join( (.) %>% distinct(gene_symbol, PValue) %>% arrange(PValue) %>% head(6)) %>%
    ggplot(aes(x = condition, y = log2(`count scaled` + 1), colour = condition)) +
    geom_jitter() +
    facet_wrap(~gene_symbol)
Joining, by = c("gene_symbol", "PValue")

Heatmaps

We can create heatmaps for the most differentially expressed genes. For example we could select the genes with FDR < 0.05 and a logFC change of 4 and make a heatmap of those.

# basic
counts.de %>%
  filter(FDR < 0.05 & abs(logFC) > 4) %>%
  heatmap(
        .horizontal = sample,
        .vertical = gene_symbol,
        .abundance = `count scaled`,
        annotation = c(immunophenotype, `developmental stage`),
        log_transform = TRUE
    )

# customised
counts.de %>%
  filter(FDR < 0.05 & abs(logFC) > 4) %>%
  heatmap(
        .horizontal = sample,
        .vertical = gene_symbol,
        .abundance = `count scaled`,
        annotation = c(immunophenotype, `developmental stage`),
        log_transform = TRUE,
        palette_abundance = c("blue", "white", "red"),
        column_names_gp = gpar(fontsize = 8)
    )

Key Points

  • RNA-Seq data can be analysed in a ‘tidy’ way using the packages tidyverse, ttBulk and tidyHeatmap
  • Key steps in an RNA-Seq analysis are filtering lowly expressed genes, normalisation for sequencing depth and composition, and testing for differential expression
  • MDS plots are very important for examining the quality of the data
  • Other useful plots for assessing RNA-Seq data are bar plots, density plots, box plots, MA plots, volcano plots, stripcharts and heatmaps
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFIgZm9yIFJOQS1TZXEiCmF1dGhvcjogIk1hcmlhIERveWxlIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIgotLS0KCiMjIEFja25vd2xlZGdlbWVudHMKVGhpcyBtYXRlcmlhbCBpcyBhZGFwdGVkIGZyb20gYW4gUiBmb3IgUk5BLVNlcSB3b3Jrc2hvcCBvcmlnaW5hbGx5IHJ1biBbaGVyZV0oaHR0cDovL2NvbWJpbmUtYXVzdHJhbGlhLmdpdGh1Yi5pby8yMDE2LTA1LTExLVJOQXNlcS8pLgogIAojIFIgZm9yIFJOQS1TZXEgY291cnNlCiAgClRoaXMgY291cnNlIGhhcyBiZWVuIGRlc2lnbmVkIHRvIGludHJvZHVjZSBiaW9sb2dpc3RzIHRvIFIgZm9yIFJOQS1TZXEgYW5hbHlzaXMuIFRoZSBmb2N1cyBoZXJlIGlzIG9uIHVzaW5nIHRpZHl2ZXJzZSB0byBhbmFseXNlIFJOQS1TZXEgZGF0YSwgYXMgd2UgYmVsaWV2ZSB0aGlzIGlzIGEgcHJvZHVjdGl2ZSBhbmQgZW5nYWdpbmcgd2F5IHRvIGxlYXJuIFIgZm9yIFJOQS1TZXEgYW5hbHlzaXMuIEluIHRoaXMgY291cnNlIHdlIHdpbGwgdXNlIHNvbWUgbmV3IHBhY2thZ2VzLCBbKip0dEJ1bGsqKl0oW2h0dHBzOi8vZ2l0aHViLmNvbS9zdGVtYW5naW9sYS90dEJ1bGspIGFuZCBbKip0aWR5SGVhdG1hcCoqXShodHRwczovL2dpdGh1Yi5jb20vc3RlbWFuZ2lvbGEvdGlkeUhlYXRtYXApLiBUaGVzZSBwYWNrYWdlcyBwcm92aWRlIGEgZnJpZW5kbHkgdGlkeXZlcnNlLXN0eWxlIHdheSB0byBwZXJmb3JtIGFuYWx5c2lzIG9mIFJOQS1TZXEgZGF0YS4KCiMgIDxpbWcgc3JjPSJpbWFnZXMvdHRidWxrX2xvZ28ucG5nIiBoZWlnaHQ9IjEzOXB4IiB3aWR0aD0iMTIwcHgiIC8+CgojIFNldHVwCgojIyBEYXRhIGZpbGVzCgpEYXRhIGZpbGVzIGFyZSBhdmFpbGFibGUgZnJvbSB0aGUgZGF0YSBmb2xkZXIgaW4gR2l0SHViIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vbWJsdWU5L3ItaW50cm8tYmlvbG9naXN0cy1ybmFzZXEpLiBZb3Ugc2hvdWxkIGRvd25sb2FkIHRoZSBmaWxlcyBsaXN0ZWQgYmVsb3cgYW5kIHBsYWNlIHRoZW0gaW50byBhIGZvbGRlciBjYWxsZWQgYGRhdGFgIGluIHlvdXIgd29ya2luZyBkaXJlY3RvcnkuCgpEYXRhIGZpbGVzOgoKKiBHU0U2MDQ1MF9HZW5lTGV2ZWxfUmF3X2RhdGEuY3N2CiogR1NFNjA0NTBfZmlsdGVyZWRfbWV0YWRhdGEuY3N2CgojIyBSIFBhY2thZ2VzCgpQYWNrYWdlcyB1c2VkOgoKKiB0aWR5dmVyc2UKKiB0dEJ1bGsKKiB0aWR5SGVhdG1hcAoqIGVkZ2VSCiogZGV2dG9vbHMKCkhlcmUgd2Ugd2lsbCB1c2UgcGFja2FnZXMgZnJvbSB0aGUgMyBtYWluIHJlcG9zaXRvcmllcyBvZiBSIHBhY2thZ2VzOiBCaW9jb25kdWN0b3IsIENSQU4gYW5kIEdpdEh1Yi4gVG8gaW5zdGFsbCB0aGUgcGFja2FnZXMgeW91IGNhbiBmb2xsb3cgdGhlIHN0ZXBzIGJlbG93LgoKIyMjIFIgdmVyc2lvbgoqIFtJbnN0YWxsIFIgPj0gMy42XShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvaW5zdGFsbC8jaW5zdGFsbC1SKS4gKipZb3UgbXVzdCBoYXZlIFIgPj0gMy42IHRvIHVzZSB0dEJ1bGsgYW5kIHRpZHlIZWF0bWFwLioqCgojIyMgQmlvY29uZHVjdG9yIHBhY2thZ2VzCiogR2V0IHRoZSBsYXRlc3QgdmVyc2lvbiBvZiBCaW9jb25kdWN0b3IgYW5kIGVkZ2VSIHBhY2thZ2UgYnkgc3RhcnRpbmcgUiBhbmQgZW50ZXJpbmcgdGhlIGNvbW1hbmRzOgpgYGB7ciwgZXZhbD1GQUxTRX0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIpKQogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQpCaW9jTWFuYWdlcjo6aW5zdGFsbCgiZWRnZVIiKQpgYGAKCiMjIyBDUkFOIHBhY2thZ2VzCkluc3RhbGwgdGhlIENSQU4gcGFja2FnZXMgd2l0aCB0aGUgY29tbWFuZCBiZWxvdy4KYGBge3IsIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoYygidGlkeXZlcnNlIiwgImRldnRvb2xzIikpCmBgYAoKIyMjIEdpdEh1YiBwYWNrYWdlcwp0dEJ1bGsgd2lsbCBiZSBhZGRlZCB0byBCaW9jb25kdWN0b3IgYW5kIHdoZW4gaXQgaXMgYWRkZWQgdGhlcmUgeW91IGNhbiBpbnN0YWxsIGl0IHVzaW5nIHRoZSB1c3VhbCBCaW9jb25kdWN0b3IgY29tbWFuZHMuIHRpZHlIZWF0bWFwIHdpbGwgYmUgYWRkZWQgdG8gQ1JBTiBhbmQgd2hlbiBpdCBpcyB0aGVyZSB5b3UgY2FuIGluc3RhbGwgaXQgd2l0aCB0aGUgaW5zdGFsbC5wYWNrYWdlcygpIGNvbW1hbmQuIEluIHRoZSBtZWFudGltZSB5b3UgY2FuIGluc3RhbGwgdHRCdWxrIGFuZCB0aWR5SGVhdG1hcCBmcm9tIHRoZWlyIGRldmVsb3BtZW50IHNpdGVzIGluIEdpdGh1YiB3aXRoIHRoZSBjb21tYW5kcyBiZWxvdy4KCmBgYHtyLCBldmFsPUZBTFNFfQojIGluc3RhbGwgdHRCdWxrCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1Yigic3RlbWFuZ2lvbGEvdHRCdWxrQGRldiIpCgojIGluc3RhbGwgdGlkeUhlYXRtYXAKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJzdGVtYW5naW9sYS90aWR5SGVhdG1hcCIpCmBgYAoKCiMgT3ZlcnZpZXcKClRPRE86IE1heWJlIGFkZCB3b3JrZmxvdyBpbWFnZSBzaG93aW5nIHN0ZXBzIGFuZCBmdW5jdGlvbnMgdXNlZAogCiogUmVhZGluZyBpbiB0YWJsZXMgb2YgY291bnRzIGFuZCBtZXRhZGF0YQoqIEZpbHRlcmluZyBhbmQgTm9ybWFsaXNhdGlvbiBvZiBjb3VudHMKKiBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwoqIFZpc3VhbGlzYXRpb24gKEhlYXRtYXBzLCBNQSBwbG90LCBWb2xjYW5vIHBsb3QpCgoKIyBJbnRyb2R1Y3Rpb24gYW5kIGRhdGEgaW1wb3J0Ck1lYXN1cmluZyBnZW5lIGV4cHJlc3Npb24gb24gYSBnZW5vbWUtd2lkZSBzY2FsZSBoYXMgYmVjb21lIGNvbW1vbiBwcmFjdGljZSBvdmVyIHRoZSBsYXN0IHR3byBkZWNhZGVzIG9yIHNvLCB3aXRoIG1pY3JvYXJyYXlzIHByZWRvbWluYW50bHkgdXNlZCBwcmUtMjAwOC4gV2l0aCB0aGUgYWR2ZW50IG9mIG5leHQgZ2VuZXJhdGlvbiBzZXF1ZW5jaW5nIHRlY2hub2xvZ3kgaW4gMjAwOCwgYW4gaW5jcmVhc2luZyBudW1iZXIgb2Ygc2NpZW50aXN0cyB1c2UgdGhpcyB0ZWNobm9sb2d5IHRvIG1lYXN1cmUgYW5kIHVuZGVyc3RhbmQgY2hhbmdlcyBpbiBnZW5lIGV4cHJlc3Npb24gaW4gb2Z0ZW4gY29tcGxleCBzeXN0ZW1zLiBBcyBzZXF1ZW5jaW5nIGNvc3RzIGhhdmUgZGVjcmVhc2VkLCB1c2luZyBSTkEtU2VxIHRvIHNpbXVsdGFuZW91c2x5IG1lYXN1cmUgdGhlIGV4cHJlc3Npb24gb2YgdGVucyBvZiB0aG91c2FuZHMgb2YgZ2VuZXMgZm9yIG11bHRpcGxlIHNhbXBsZXMgaGFzIG5ldmVyIGJlZW4gZWFzaWVyLiBUaGUgY29zdCBvZiB0aGVzZSBleHBlcmltZW50cyBoYXMgbm93IG1vdmVkIGZyb20gZ2VuZXJhdGluZyB0aGUgZGF0YSB0byBzdG9yaW5nIGFuZCBhbmFseXNpbmcgaXQuCgpUaGVyZSBhcmUgbWFueSBzdGVwcyBpbnZvbHZlZCBpbiBhbmFseXNpbmcgYW4gUk5BLVNlcSBleHBlcmltZW50LiBBbmFseXNpbmcgYW4gUk5Bc2VxIGV4cGVyaW1lbnQgYmVnaW5zIHdpdGggc2VxdWVuY2luZyByZWFkcy4gVGhlc2UgYXJlIGFsaWduZWQgdG8gYSByZWZlcmVuY2UgZ2Vub21lLCB0aGVuIHRoZSBudW1iZXIgb2YgcmVhZHMgbWFwcGVkIHRvIGVhY2ggZ2VuZSBjYW4gYmUgY291bnRlZC4gVGhpcyByZXN1bHRzIGluIGEgdGFibGUgb2YgY291bnRzLCB3aGljaCBpcyB3aGF0IHdlIHBlcmZvcm0gc3RhdGlzdGljYWwgYW5hbHlzZXMgb24gaW4gUi4gV2hpbGUgbWFwcGluZyBhbmQgY291bnRpbmcgYXJlIGltcG9ydGFudCBhbmQgbmVjZXNzYXJ5IHRhc2tzLCB0b2RheSB3ZSB3aWxsIGJlIHN0YXJ0aW5nIGZyb20gdGhlIGNvdW50IGRhdGEgYW5kIGdldHRpbmcgc3R1Y2sgaW50byBhbmFseXNpcy4KCkZpcnN0LCBsZXTigJlzIGxvYWQgYWxsIHRoZSBwYWNrYWdlcyB3ZSB3aWxsIG5lZWQgdG8gYW5hbHlzZSB0aGUgZGF0YS4KCmBgYHtyICwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBsb2FkIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0dEJ1bGspCmxpYnJhcnkodGlkeUhlYXRtYXApCmBgYAoKIyMgR1JFSU4gcmVwb3NpdG9yeSBvZiBSTkEtU2VxIGRhdGFzZXRzCkluIHRoaXMgdHV0b3JpYWwsIHdlIHdpbGwgbGVhcm4gc29tZSBSIHRocm91Z2ggY3JlYXRpbmcgcGxvdHMgdG8gdmlzdWFsaXNlIGRhdGEgZnJvbSBhbiBSTkEtU2VxIGV4cGVyaW1lbnQuIFJOQS1TZXEgY291bnRzIGZpbGUgY2FuIGJlIG9idGFpbmVkIGZyb20gdGhlIFtHUkVJTiBwbGF0Zm9ybV0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1OTgtMDE5LTQzOTM1LTgpLiBHUkVJTiBzdGFuZHMgZm9yIEdFTyBSTkEtU2VxIEV4cGVyaW1lbnRzIEludGVyYWN0aXZlIE5hdmlnYXRvciBhbmQgcHJvdmlkZXMgPjYsNTAwIHB1Ymxpc2hlZCBkYXRhc2V0cyBmcm9tIEdFTyB0aGF0IGhhdmUgYmVlbiB1bmlmb3JtbHkgcHJvY2Vzc2VkLiBJdCBpcyBhdmFpbGFibGUgYXQgaHR0cDovL3d3dy5pbGluY3Mub3JnL2FwcHMvZ3JlaW4vLiBZb3UgY2FuIHNlYXJjaCBmb3IgYSBkYXRhc2V0IG9mIGludGVyZXN0IHVzaW5nIHRoZSBHRU8gY29kZS4gV2Ugb2J0YWluZWQgdGhlIGRhdGFzZXQgdXNlZCBoZXJlIHVzaW5nIHRoZSBjb2RlIEdTRTYwNDUwLiBHUkVJTiBwcm92aWRlIFFDIG1ldHJpY3MgZm9yIHRoZSBSTkEtU2VxIGRhdGFzZXRzIGFuZCBib3RoIHJhdyBhbmQgbm9ybWFsaXNlZCBjb3VudHMuIFdlIHdpbGwgdXNlIHRoZSByYXcgY291bnRzIGhlcmUuIEdlbmVyYWxseSwgdGhlIGhpZ2hlciB0aGUgbnVtYmVyIG9mIGNvdW50cyB0aGUgbW9yZSB0aGUgZ2VuZSBpcyBleHByZXNzZWQuCgojIyBNb3VzZSBtYW1tYXJ5IGdsYW5kIGRhdGFzZXQKSGVyZSB3ZSB3aWxsIHBlcmZvcm0gUk5BLVNlcSBhbmFseXNpcyB1c2luZyBkYXRhIGZyb20gYSBicmVhc3QgY2FuY2VyIHJlc2VhcmNoIHN0dWR5LCBmcm9tIHRoZSBwYXBlciBieSBbRnUgZXQgYWwuIDIwMTVdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcHVibWVkLzI1NzMwNDcyKSwgR0VPIGNvZGUgR1NFNjA0NTAuIFRoaXMgc3R1ZHkgZXhhbWluZWQgZ2VuZSBleHByZXNzaW9uIGluIGJhc2FsIGFuZCBsdW1pbmFsIGNlbGxzIGZyb20gbWljZSBhdCBkaWZmZXJlbnQgc3RhZ2VzIG9mIG1hbW1hcnkgZ2xhbmQgZGV2ZWxvcG1lbnQgKHZpcmdpbiwgcHJlZ25hbnQgYW5kIGxhY3RhdGluZykuIFRoZXJlIGFyZSAyIHNhbXBsZXMgcGVyIGdyb3VwIGFuZCA2IGdyb3VwcywgMTIgc2FtcGxlcyBpbiB0b3RhbC4KCiFbXShpbWFnZXMvbW91c2VfZXhwLnBuZykKCiMjIFJlYWRpbmcgaW4gdGhlIGRhdGEKCipTZXQgdXAgYW4gUlN0dWRpbyBwcm9qZWN0IHNwZWNpZnlpbmcgdGhlIGRpcmVjdG9yeSB3aGVyZSB5b3UgaGF2ZSBzYXZlZCB0aGUgYC9kYXRhYCBkaXJlY3RvcnkqLgpPcGVuIGEgbmV3IHNjcmlwdCBmb3IgdGhpcyB3b3Jrc2hvcCBGaWxlID4gTmV3IEZpbGUgPiBSIFNjcmlwdC4gU2F2ZSBpdCBhcyBlLmcuIGludHJvLXJuYXNlcS5SLgoKCmBgYHtyfQojIHJlYWQgaW4gY291bnRzIGZpbGUKY291bnRzIDwtIHJlYWRfY3N2KCJkYXRhL0dTRTYwNDUwX0dlbmVMZXZlbF9SYXdfZGF0YS5jc3YiKQoKIyByZWFkIGluIG1ldGFkYXRhCnNhbXBsZWluZm8gPC0gcmVhZF9jc3YoImRhdGEvR1NFNjA0NTBfZmlsdGVyZWRfbWV0YWRhdGEuY3N2IikKYGBgCgpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgZGF0YS4gWW91IGNhbiB0eXBlIHRoZSBuYW1lIG9mIHRoZSBvYmplY3QgdG8gdmlldyB0aGUgZmlyc3QgZmV3IGxpbmVzIGFuZCB0byBzZWUgaG93IG1hbnkgcm93cyBhbmQgY29sdW1ucyBpdCBoYXMuCgpgYGB7cn0KY291bnRzCmBgYApUaGUgYGNvdW50c2Agb2JqZWN0IGNvbnRhaW5zIGluZm9ybWF0aW9uIGFib3V0IGdlbmVzIChvbmUgZ2VuZSBwZXIgcm93KSwgdGhlIGZpcnN0IGNvbHVtbiBoYXMgdGhlIEVuc2VtYmwgZ2VuZSBpZCwgdGhlIHNlY29uZCBoYXMgdGhlIGdlbmUgc3ltYm9sIGFuZCB0aGUgcmVtYWluaW5nIGNvbHVtbnMgY29udGFpbiBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbnVtYmVyIG9mIHJlYWRzIGFsaWduaW5nIHRvIHRoZSBnZW5lIGluIGVhY2ggZXhwZXJpbWVudGFsIHNhbXBsZS4gTm90ZSB0aGUgZ2VuZSBjb3VudHMgaGVyZSBhcmUgbm90IGludGVnZXJzIGFzIHRoZXkncmUgZXN0aW1hdGVkIGNvdW50cyBmcm9tIHNhbG1vbiAoc2VlIGhlcmU6IGh0dHBzOi8vc3VwcG9ydC5iaW9jb25kdWN0b3Iub3JnL3AvMTAxMTU2LykuIFRoZXJlIGFyZSB0d28gcmVwbGljYXRlcyBmb3IgZWFjaCBjZWxsIHR5cGUgYW5kIHRpbWUgcG9pbnQgKGRldGFpbGVkIHNhbXBsZSBpbmZvIGNhbiBiZSBmb3VuZCBpbiBmaWxlICJHU0U2MDQ1MF9zZXJpZXNfbWF0cml4LnR4dCIgZnJvbSB0aGUgW0dFTyB3ZWJzaXRlXShodHRwOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvZ2VvL3F1ZXJ5L2FjYy5jZ2k/YWNjPUdTRTYwNDUwKSkuIFRoZSBgc2FtcGxlaW5mb2AgbWV0YWRhdGEgZmlsZSBjb250YWlucyBiYXNpYyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2FtcGxlcyB0aGF0IHdlIHdpbGwgbmVlZCBmb3IgdGhlIGFuYWx5c2lzIHRvZGF5LgoKRmlyc3Qgd2Ugd2lsbCBjb252ZXJ0IHRoZSBjb3VudHMgaW50byBsb25nIGZvcm1hdCAodGlkeSBmb3JtYXQpLCBzaW1pbGFyIHRvIHdoYXQgd2UgZGlkIGluIHRoZSBJbnRybyB0byBSIHNlc3Npb24uCmBgYHtyfQojIGNvbnZlcnQgdG8gdGlkeSBmb3JtYXQKY291bnRzIDwtIHBpdm90X2xvbmdlcihjb3VudHMsIGNvbHMgPSBzdGFydHNfd2l0aCgiR1NNIiksIG5hbWVzX3RvID0gInNhbXBsZSIsIHZhbHVlc190byA9ICJjb3VudCIpIAoKIyB0YWtlIGEgbG9vawpjb3VudHMKYGBgCgpXZSB3aWxsIG5leHQgZXh0cmFjdCBqdXN0IHRoZSBjb2x1bW5zIHdlIG5lZWQsIHNhbXBsZSwgZ2VuZV9zeW1ib2wsIGNvdW50LiBUbyBkbyB0aGlzIHdlIHdpbGwgdXNlIHRoZSB0aWR5dmVyc2UgcGlwZSBgJT4lYC4gVGhpcyAncGlwZXMnIHRoZSBvdXRwdXQgZnJvbSB0aGUgY29tbWFuZCBvbiB0aGUgbGVmdCBpbnRvIHRoZSBjb21tYW5kIG9uIHRoZSByaWdodC9iZWxvdy4gVXNpbmcgdGhlIHBpcGUgaXMgbm90IGVzc2VudGlhbCBidXQgaXQgcmVkdWNlcyB0aGUgYW1vdW50IG9mIGNvZGUgd2UgbmVlZCB0byB3cml0ZSB3aGVuIHdlIGhhdmUgbXVsdGlwbGUgc3RlcHMgKGFzIHdlJ2xsIHNlZSBsYXRlcikuIEl0IGFsc28gY2FuIG1ha2UgdGhlIHN0ZXBzIGNsZWFyZXIgYW5kIGVhc2llciB0byBzZWUuICBGb3IgbW9yZSBkZXRhaWxzIG9uIHRoZSBwaXBlIHNlZSBbaGVyZV0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9waXBlcy5odG1sKS4KCmBgYHtyfQojIHVzaW5nIHBpcGUKY291bnRzIDwtIGNvdW50cyAlPiUgCiAgc2VsZWN0KHNhbXBsZSwgZ2VuZV9zeW1ib2wsIGNvdW50LCBYMSkKCiMgdGFrZSBhIGxvb2sgCmNvdW50cwpgYGAKClRha2UgYSBsb29rIGF0IHRoZSBzYW1wbGVpbmZvIGZpbGUuIFRoZSBmaXJzdCBjb2x1bW4gIlgxIiBjb250YWlucyB0aGUgc2FtcGxlIGlkcywgdGhlIHNlY29uZCAiY2hhcmFjdGVyaXN0aWNzIiBjb250YWlucyB0aGUgc3BlY2lmaWMgZ3JvdXAgdGhlIHNhbXBsZSBiZWxvbmdzIHRvIChlLmcuIG1hbW1hcnkgZ2xhbmQsIGx1bWluYWwgY2VsbHMsIHZpcmdpbiksIHRoZSB0aGlyZCBjb2x1bW4gImltbXVub3BoZW5vdHlwZSIgY29udGFpbnMganVzdCB0aGUgY2VsbCB0eXBlIChsdW1pbmFsIG9yIGJhc2FsKSBhbmQgdGhlIGZvdXJ0aCBjb2x1bW4gImRldmVsb3BtZW50YWwgc3RhZ2UiIGNvbnRhaW5zIGp1c3QgdGhlIHN0YWdlICh2aXJnaW4sIHByZWduYW50IG9yIGxhY3RhdGluZykuCgpgYGB7cn0Kc2FtcGxlaW5mbwpgYGAKCldlIHdhbnQgdG8gY29tcGFyZSB0aGUgZ3JvdXBzIGluIHRoZSAiY2hhcmFjdGVyaXN0aWNzIiBjb2x1bW4gaG93ZXZlciB0aGUgbmFtZXMgYXJlIHF1aXRlIGxvbmcgc28sIHNpbWlsYXIgdG8gd2hhdCB3ZSBkaWQgaW4gdGhlIEludHJvIHRvIFIgc2Vzc2lvbiwgd2UnbGwgbWFrZSBhIGNvbHVtbiBjb250YWluaW5nIHNob3J0ZXIgZ3JvdXAgbmFtZXMuCgoKYGBge3J9CiMgbWFrZSBjb2x1bW4gY2FsbGVkIGNvbmRpdGlvbiB3aXRoIHNob3J0ZXIgZ3JvdXAgbmFtZXMKc2FtcGxlaW5mbyA8LSBtdXRhdGUoc2FtcGxlaW5mbywgY29uZGl0aW9uID0gY2FzZV93aGVuKCAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QoY2hhcmFjdGVyaXN0aWNzLCAiYmFzYWwuKnZpcmdpbiIpIH4gICJidmlyZyIsCiAgICAgICAgc3RyX2RldGVjdChjaGFyYWN0ZXJpc3RpY3MsICJiYXNhbC4qcHJlZyIpICB+ICAiYnByZWciLAogICAgICAgIHN0cl9kZXRlY3QoY2hhcmFjdGVyaXN0aWNzLCAiYmFzYWwuKmxhY3QiKSAgfiAgImJsYWN0IiwKICAgICAgICBzdHJfZGV0ZWN0KGNoYXJhY3RlcmlzdGljcywgImx1bWluYWwuKnZpcmdpbiIpICB+ICAibHZpcmciLAogICAgICAgIHN0cl9kZXRlY3QoY2hhcmFjdGVyaXN0aWNzLCAibHVtaW5hbC4qcHJlZyIpICB+ICAibHByZWciLAogICAgICAgIHN0cl9kZXRlY3QoY2hhcmFjdGVyaXN0aWNzLCAibHVtaW5hbC4qbGFjdCIpICB+ICAibGxhY3QiCiAgICAgICApKQoKc2FtcGxlaW5mbwpgYGAKCk5vdyB3ZSBoYXZlIG91ciBjb3VudHMgbWF0cml4IGluIHRoZSBsb25nIGZvcm1hdCB3ZSB3aWxsIGpvaW4gaXQgdG8gb3VyIHNhbXBsZWluZm8gc28gd2UgaGF2ZSBpbmZvcm1hdGlvbiBvbiB0aGUgc2FtcGxlcywgd2hhdCBncm91cHMgdGhleSBiZWxvbmcgdG8uIFRoaXMgaXMgc2ltaWxhciB0byB3aGF0IHdlIGRpZCBpbiB0aGUgSW50cm8gdG8gUiBzZXNzaW9uLgoKYGBge3J9CmNvdW50cyA8LSBmdWxsX2pvaW4oY291bnRzLCBzYW1wbGVpbmZvLCBieSA9IGMoInNhbXBsZSIgPSAiWDEiKSkKCiMgdGFrZSBhIGxvb2sKY291bnRzCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBvdXIgZGF0YSBpbiB0aGUgZm9ybWF0IHdlIHdhbnQgd2Ugd2lsbCBjcmVhdGUgYSB0dEJ1bGsgb2JqZWN0LCB0aGF0IHdlIGNhbiB1c2UgdG8gcGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyB3aXRoIHRoZSB0dEJ1bGsgcGFja2FnZS4gRm9yIHRoaXMgd2UgbmVlZCB0byBzcGVjaWZ5IG91ciBjb3VudHMgb2JqZWN0IGFuZCB0aGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgdGhhdCBjb250YWluIG91ciBzYW1wbGUgaWRzLCBvdXIgZ2VuZSBpZGVudGlmaWVycyBhbmQgb3VyIGNvdW50cy4gQW55IG90aGVyIGNvbHVtbnMgaW4gdGhlIGNvdW50cyBvYmplY3QgZS5nLiBvdXIgRW5zZW1ibCBnZW5lIGlkICJYMSIgY29sdW1uIHdpbGwgcmVtYWluIGF0IHRoZSBlbmQuCmBgYHtyfQojY3JlYXRlIGEgJ3R0JyBvYmplY3QKY291bnRzIDwtIHR0QnVsayhjb3VudHMsIHNhbXBsZSwgZ2VuZV9zeW1ib2wsIGNvdW50KQoKIyB0YWtlIGEgbG9vawpjb3VudHMKYGBgClNvbWUgZ2VuZSBzeW1ib2xzIGFyZSBub3QgdW5pcXVlLCB0aGV5IG1hcCB0byBtb3JlIHRoYW4gb25lIGdlbmUgaWQuIFdlIG5lZWQgdG8gcmVtb3ZlIHRoaXMgcmVkdW5kYW5jeSAgYW5kIHdlIGNhbiBkbyB0aGF0IHdpdGggdHRCdWxrIGZ1bmN0aW9uIGFnZ3JlZ2F0ZV9kdXBsaWNhdGVzKCkuIEJ5IGRlZmF1bHQgaXQgd2lsbCBhZ2dyZWdhdGUgZHVwbGljYXRlIGdlbmUgc3ltYm9scyBzdW1taW5nIHRoZWlyIGNvdW50cy4gQWRkIHdheSB0byBpZGVudGlmeSB3aGF0IHRoZSBkdXBsaWNhdGVzIGFyZT8gIAoKYGBge3J9CiMgZ2V0IHJpZCBvZiBkdXBsaWNhdGVkIGdlbmUgc3ltYm9scwpjb3VudHMgPC0gYWdncmVnYXRlX2R1cGxpY2F0ZXMoY291bnRzKQpgYGAKCgojIEZpbHRlcmluZyBhbmQgTm9ybWFsaXNhdGlvbgoKQmVmb3JlIHRlc3RpbmcgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHdlIG5lZWQgdG8gZG8gc29tZSBwcmVwcm9jZXNzaW5nIG9mIHRoZSBkYXRhLiBXZSBmaWx0ZXIgbG93bHkgZXhwcmVzc2VkIGdlbmVzIGFuZCBhbHNvIG5vcm1hbGlzZSBmb3IgZGlmZmVyZW5jZXMgaW4gc2VxdWVuY2luZyBkZXB0aCBhbmQgY29tcG9zaXRpb24gYmV0d2VlbiB0aGUgc2FtcGxlcy4gVGhlc2Ugc3RlcHMgYXJlIGV4cGxhaW5lZCBpbiBtb3JlIGRldGFpbHMgYmVsb3cuCgpGaXJzdCB3ZSB3aWxsIGNoZWNrIGhvdyBtYW55IGNvdW50cyB3ZSBoYXZlIGZvciBlYWNoIHNhbXBsZS4gV2UgY2FuIGRvIHRoaXMgcXVpdGUgZWFzaWx5IGJ5IG1ha2luZyBhIGJhciBwbG90LiBUaGlzIGhlbHBzIHVzIHNlZSB3aGV0aGVyIHRoZXJlIGFyZSBhbnkgbWFqb3IgZGlzY3JlcGFuY2llcyBiZXR3ZWVuIHRoZSBzYW1wbGVzIG1vcmUgZWFzaWx5LgoKYGBge3J9CiMgbWFrZSBiYXJwbG90IG9mIGNvdW50cwpnZ3Bsb3QoY291bnRzLCBhZXMoeD1zYW1wbGUsIHdlaWdodD1jb3VudCwgZmlsbD1jb25kaXRpb24pKSArIAogIGdlb21fYmFyKCkKYGBgCgpUaGUgYmFyIHBsb3RzIHNob3cgdXMgdGhlcmUgYXJlIH4yMCBtaWxsaW9uIGNvdW50cyBwZXIgc2FtcGxlLgoKIyMjIEZpbHRlcmluZyBsb3dseSBleHByZXNzZWQgZ2VuZXMgIApHZW5lcyB3aXRoIHZlcnkgbG93IGNvdW50cyBhY3Jvc3MgYWxsIGxpYnJhcmllcyBwcm92aWRlIGxpdHRsZSBldmlkZW5jZSBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5kIHRoZXkgaW50ZXJmZXJlIHdpdGggc29tZSBvZiB0aGUgc3RhdGlzdGljYWwgYXBwcm94aW1hdGlvbnMgdGhhdCBhcmUgdXNlZCBsYXRlciBpbiB0aGUgcGlwZWxpbmUuIFRoZXkgYWxzbyBhZGQgdG8gdGhlIG11bHRpcGxlIHRlc3RpbmcgYnVyZGVuIHdoZW4gZXN0aW1hdGluZyBmYWxzZSBkaXNjb3ZlcnkgcmF0ZXMsIHJlZHVjaW5nIHBvd2VyIHRvIGRldGVjdCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFRoZXNlIGdlbmVzIHNob3VsZCBiZSBmaWx0ZXJlZCBvdXQgcHJpb3IgdG8gZnVydGhlciBhbmFseXNpcy4KClRoZXJlIGFyZSBhIGZldyB3YXlzIHRvIGZpbHRlciBvdXQgbG93bHkgZXhwcmVzc2VkIGdlbmVzLiBXaGVuIHRoZXJlIGFyZSBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMgaW4gZWFjaCBncm91cCwgd2UgZmF2b3VyIGZpbHRlcmluZyBvbiBhIG1pbmltdW0gY291bnQgdGhyZXNob2xkIGluIHRoZSBzbWFsbGVzdCBncm91cCBzaXplLiBUaGlzIGVuc3VyZXMgdGhhdCBhIGdlbmUgd2lsbCBiZSByZXRhaW5lZCBpZiBpdCBpcyBvbmx5IGV4cHJlc3NlZCBpbiBvbmUgZ3JvdXAuIEluIHRoaXMgZGF0YXNldCwgd2UgY2hvb3NlIHRvIHJldGFpbiBnZW5lcyBpZiB0aGV5IGFyZSBleHByZXNzZWQgYXQgYSBjb3VudHMtcGVyLW1pbGxpb24gKENQTSkgYWJvdmUgMC41IGluIGF0IGxlYXN0IHR3byBzYW1wbGVzIChvdXIgc21hbGxlc3QgZ3JvdXAgc2l6ZSkuIEFzIGEgZ2VuZXJhbCBydWxlLCBhIGdvb2QgQ1BNIHRocmVzaG9sZCBjYW4gYmUgY2hvc2VuIGJ5IGlkZW50aWZ5aW5nIHRoZSBDUE0gdGhhdCBjb3JyZXNwb25kcyB0byBhIGNvdW50IG9mIDEwLCB3aGljaCBpbiB0aGlzIGNhc2UgaXMgYWJvdXQgMC41IGFzIHRoZXJlIGFyZSB+MjAgbWlsbGlvbiBjb3VudHMgcGVyIHNhbXBsZSAoMjAvMTAgPSAwLjUgQ1BNKS4gSWYgdGhlIGNvdW50IGlzIGFueSBzbWFsbGVyLCBpdCBpcyBjb25zaWRlcmVkIHRvIGJlIHZlcnkgbG93LCBpbmRpY2F0aW5nIHRoYXQgdGhlIGFzc29jaWF0ZWQgZ2VuZSBpcyBub3QgZXhwcmVzc2VkIGluIHRoYXQgc2FtcGxlLiBTbWFsbGVyIENQTSB0aHJlc2hvbGRzIGFyZSB1c3VhbGx5IGFwcHJvcHJpYXRlIGZvciBsYXJnZXIgbGlicmFyaWVzLiBTbyBmb3IgdGhpcyBkYXRhc2V0IHdlIHdpbGwgZmlsdGVyIGxvd2x5IGV4cHJlc3NlZCBnZW5lcyB1c2luZyBhIGBjcG1fdGhyZXNob2xkYCBvZiAwLjUgYW5kIGEgYHByb3BfdGhyZXNob2xkYCAocHJvcG9ydGlvbiBvZiBzYW1wbGVzKSBvZiAyLzEyIHdpdGggdHRCdWxrLgoKIyMjIE5vcm1hbGlzYXRpb24gZm9yIHNlcXVlbmNpbmcgZGVwdGggYW5kIGNvbXBvc2l0aW9uCgpUTU0gbm9ybWFsaXNhdGlvbiBpcyBwZXJmb3JtZWQgdG8gZWxpbWluYXRlIGNvbXBvc2l0aW9uIGJpYXNlcyBiZXR3ZWVuIGxpYnJhcmllcyBbQHJvYmluc29uMjAxMHRtbV0uIFRoaXMgZ2VuZXJhdGVzIGEgc2V0IG9mIG5vcm1hbGlzYXRpb24gZmFjdG9ycywgd2hlcmUgdGhlIHByb2R1Y3Qgb2YgdGhlc2UgZmFjdG9ycyBhbmQgdGhlIGxpYnJhcnkgc2l6ZXMgZGVmaW5lcyB0aGUgZWZmZWN0aXZlIGxpYnJhcnkgc2l6ZS4gVE1NIG5vcm1hbGlzYXRpb24gKGFuZCBtb3N0IHNjYWxpbmcgbm9ybWFsaXNhdGlvbiBtZXRob2RzKSBzY2FsZSByZWxhdGl2ZSB0byBvbmUgc2FtcGxlLgoKSW4gdGhlIHR0QnVsayBwYWNrYWdlIHRoZSBmdW5jdGlvbiBzY2FsZV9hYnVuZGFuY2UoKSBwZXJmb3JtcyB0aGUgZmlsdGVyaW5nIGFuZCBub3JtYWxpc2F0aW9uIHRvIGdlbmVyYXRlIG5vcm1hbGlzZWQgY291bnRzLgoKYGBge3J9CiMgTm9ybWFsaXNhdGlvbiBmb3IgbGlicmFyeSBzaXplIGFuZCBjb21wb3NpdGlvbiBiaWFzIChzY2FsZSBjb3VudHMpLCAKY291bnRzLm5vcm0gPC0gY291bnRzICU+JSBzY2FsZV9hYnVuZGFuY2UoCiAgY3BtX3RocmVzaG9sZCA9IDAuNSwKICBwcm9wX3RocmVzaG9sZCA9IDIvMTIpCgojIHRha2UgYSBsb29rCmNvdW50cy5ub3JtCmBgYAoKQWZ0ZXIgd2UgcnVuIHNjYWxlX2FidW5kYW5jZSgpIHdlIHNob3VsZCBzZWUgc29tZSBjb2x1bW5zIGhhdmUgYmVlbiBhZGRlZCB0byB0aGUgZW5kIG9mIGNvdW50cy4gV2UgaGF2ZSBhIGNvbHVtbiBjYWxsZWQgYGZpbHRlciBvdXQgbG93IGNvdW50c2AgdGhhdCBpbmRpY2F0ZXMgd2hldGhlciB0aGUgZ2VuZSBoYXMgYmVlbiBmaWx0ZXJlZCBkdWUgdG8gYmVpbmcgbG93bHkgZXhwcmVzc2VkLCBGQUxTRSBtZWFucyBpdCB3YXNuJ3QgZmlsdGVyZWQsIFRSVUUgbWVhbnMgaXQgd2FzLiBUaGUgYGNvdW50IHNjYWxlZGAgY29sdW1uIGNvbnRhaW5zIHRoZSBzY2FsZWQgY291bnRzLCBhZnRlciB0aGUgbm9ybWFsaXNhdGlvbiBoYXMgYmVlbiBhcHBsaWVkLgoKKk1heWJlIGFkZCBhIHdheSB0byBzZWUgaG93IG1hbnkgZ2VuZXMgd2UgaGF2ZSBmb3IgZWFjaCBzYW1wbGUsIGJlZm9yZSBhbmQgYWZ0ZXIgZmlsdGVyaW5nPyoKCldlIGNhbiBjcmVhdGUgZGVuc2l0eSBwbG90cyB0byB2aWV3IHRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSBjb3VudHMgZm9yIHRoZSBzYW1wbGVzIGJlZm9yZSBhbmQgYWZ0ZXIgZmlsdGVyaW5nLiBUaGlzIGlzIGFsc28gYSBxdWFsaXR5IGNoZWNrIHRvIHNlZSBpZiB0aGUgc2FtcGxlcyBsb29rIHNpbWlsYXIgYW5kIHRoYXQgbm9uZSBsb29rIG1ham9ybHkgZGlmZmVyZW50LiBOb3RlIHdlIG5lZWQgdG8gdGFrZSB0aGUgbG9nMiBvZiB0aGUgY291bnRzIGFuZCBhZGQgYSBzbWFsbCBvZmZzZXQgKDEpIHRvIGF2b2lkIHRha2luZyBsb2cgb2YgemVyby4KCmBgYHtyfQojIGRlbnNpdHkgcGxvdCBiZWZvcmUgCmNvdW50cy5ub3JtICU+JSAKICAgIGdncGxvdChhZXMoeD1sb2cyKGBjb3VudCBzY2FsZWRgICsgMSksIGdyb3VwPXNhbXBsZSwgY29sb3I9Y29uZGl0aW9uKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkKYGBgClRoZSBsYXJnZSBwZWFrIG9uIHRoZSBsZWZ0IG9mIHRoZSBwbG90IHNob3dzIHRoYXQgYSBsYXJnZSBwcm9wb3J0aW9uIG9mIGdlbmVzIHdpdGhpbiBlYWNoIHNhbXBsZSBhcmUgbm90IGV4cHJlc3NlZCBvciBsb3dseS1leHByZXNzZWQuCgojIyMjIEV4ZXJjaXNlCkFkYXB0IHRoZSBjb2RlIGFib3ZlIHRvIGNyZWF0ZSBhIGRlbnNpdHkgcGxvdCBvZiB0aGUgY291bnRzIGFmdGVyIGZpbHRlcmluZyBsb3dseSBleHByZXNzZWQgZ2VuZXMuIEhvdyBkb2VzIGl0IGNvbXBhcmUgaXQgdG8gdGhlIGRlbnNpdHkgcGxvdCBhYm92ZT8KCmBgYHtyfQojIFNvbHV0aW9uCmNvdW50cy5ub3JtICU+JSAKICBmaWx0ZXIoYGZpbHRlciBvdXQgbG93IGNvdW50c2AgPT0gRkFMU0UpICU+JQogICAgZ2dwbG90KGFlcyh4PWxvZzIoYGNvdW50IHNjYWxlZGArMSksIGdyb3VwPXNhbXBsZSwgY29sb3I9Y29uZGl0aW9uKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkKYGBgCgpXZSBjYW4gYWxzbyBjcmVhdGUgYm94IHBsb3RzIHRvIGNoZWNrIHRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSBjb3VudHMgaW4gdGhlIHNhbXBsZXMuIFdlIGNhbiBhZGQgYSBsaW5lIHRocm91Z2ggdGhlIG1lZGlhbiB0byBoZWxwIHVzIHNlZSBob3cgc2ltaWxhciAob3Igbm90KSB0aGUgZGlzdHJpYnV0aW9ucyBhcmUuCgpgYGB7cn0KIyBib3ggcGxvdCBiZWZvcmUgc2NhbGluZwpjb3VudHMubm9ybSAlPiUgCiAgZmlsdGVyKGBmaWx0ZXIgb3V0IGxvdyBjb3VudHNgID09IEZBTFNFKSAlPiUKICAgIGdncGxvdChhZXMoeD1zYW1wbGUsIHk9bG9nMihjb3VudCsxKSwgZmlsbD1jb25kaXRpb24pKSArCiAgICBnZW9tX2JveHBsb3QoKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1lZGlhbihsb2cyKGNvdW50KzEpKSwgY29sb3VyID0gJ3JlZCcpKQpgYGAKCiMjIyMgRXhlcmNpc2UKQWRhcHQgdGhlIGNvZGUgYWJvdmUgdG8gY3JlYXRlIGJveCBwbG90cyB0aGF0IG9ubHkgaW5jbHVkZXMgdGhlIGdlbmVzIHRoYXQgaGF2ZSBub3QgYmVlbiBmaWx0ZXJlZCBkdWUgdG8gbG93IGNvdW50cy4gSG93IGRvZXMgaXQgY29tcGFyZSBpdCB0byB0aGUgYm94IHBsb3RzIGFib3ZlPwoKYGBge3J9CiMgYm94IHBsb3QgYWZ0ZXIgZmlsdGVyaW5nCmNvdW50cy5ub3JtICU+JSAKICBmaWx0ZXIoYGZpbHRlciBvdXQgbG93IGNvdW50c2AgPT0gRkFMU0UpICU+JQogICAgZ2dwbG90KGFlcyh4PXNhbXBsZSwgeT1sb2cyKGBjb3VudCBzY2FsZWRgKzEpLCBmaWxsPWNvbmRpdGlvbikpICsKICAgIGdlb21fYm94cGxvdCgpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWVkaWFuKGxvZzIoYGNvdW50IHNjYWxlZGArMSkpLCBjb2xvdXIgPSAncmVkJykpCmBgYAoKIyBNdWx0aWRpbWVuc2lvbmFsIHNjYWxpbmcgcGxvdHMKCkJ5IGZhciwgb25lIG9mIHRoZSBtb3N0IGltcG9ydGFudCBwbG90cyB3ZSBtYWtlIHdoZW4gd2UgYW5hbHlzZSBSTkEtU2VxIGRhdGEgYXJlIE1EUyBwbG90cy4gQW4gTURTIHBsb3QgaXMgYSB2aXN1YWxpc2F0aW9uIG9mIGEgcHJpbmNpcGFsIGNvbXBvbmVudHMgYW5hbHlzaXMsIHdoaWNoIGRldGVybWluZXMgdGhlIGdyZWF0ZXN0IHNvdXJjZXMgb2YgdmFyaWF0aW9uIGluIHRoZSBkYXRhLiBBIHByaW5jaXBhbCBjb21wb25lbnRzIGFuYWx5c2lzIGlzIGFuIGV4YW1wbGUgb2YgYW4gdW5zdXBlcnZpc2VkIGFuYWx5c2lzLCB3aGVyZSB3ZSBkb24ndCBuZWVkIHRvIHNwZWNpZnkgdGhlIGdyb3Vwcy4gSWYgeW91ciBleHBlcmltZW50IGlzIHdlbGwgY29udHJvbGxlZCBhbmQgaGFzIHdvcmtlZCB3ZWxsLCB3aGF0IHdlIGhvcGUgdG8gc2VlIGlzIHRoYXQgdGhlIGdyZWF0ZXN0IHNvdXJjZXMgb2YgdmFyaWF0aW9uIGluIHRoZSBkYXRhIGFyZSB0aGUgdHJlYXRtZW50cy9ncm91cHMgd2UgYXJlIGludGVyZXN0ZWQgaW4uIEl0IGlzIGFsc28gYW4gaW5jcmVkaWJseSB1c2VmdWwgdG9vbCBmb3IgcXVhbGl0eSBjb250cm9sIGFuZCBjaGVja2luZyBmb3Igb3V0bGllcnMuIFdlIGNhbiB1c2UgdGhlIGByZWR1Y2VfZGltZW5zaW9ucygpYCBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGRpbWVuc2lvbnMuCgoKYGBge3J9CiMgZ2V0IE1EUyBkaW1lbnNpb25zCmNvdW50cy5ub3JtLk1EUyA8LQogIGNvdW50cy5ub3JtICU+JQogIHJlZHVjZV9kaW1lbnNpb25zKG1ldGhvZD0iTURTIiwgLmRpbXMgPSAyKQoKIyB0YWtlIGEgbG9vawpjb3VudHMubm9ybS5NRFMKYGBgCgpUaGVuIHdlIGNhbiBzZWxlY3QganVzdCB0aGUgZGltZW5zaW9ucyBmb3IgdGhlIHNhbXBsZXMuCgpgYGB7cn0KIyBnZXQgdGhlIGRpbWVuc2lvbnMgd2l0aCBhbGwgbWV0YWRhdGEKTURTZGltcyA8LSBjb3VudHMubm9ybS5NRFMgJT4lCnNlbGVjdChjb250YWlucygiRGltIiksIHNhbXBsZSwgaW1tdW5vcGhlbm90eXBlLCBgZGV2ZWxvcG1lbnRhbCBzdGFnZWAsIGNvbmRpdGlvbikgJT4lCmRpc3RpbmN0KCkKCiMgdGFrZSBhIGxvb2sKTURTZGltcwpgYGAKCk5leHQgd2UgY2FuIHBsb3QgdGhlIE1EUyBkaW1lbnNpb25zIGFzIGEgc2NhdHRlcnBsb3QuCgpgYGB7cn0KIyBNRFMgcGxvdApnZ3Bsb3QoTURTZGltcywgYWVzKHg9RGltMSwgeT1EaW0yLCBjb2xvdXI9c2FtcGxlKSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCgojIyMjIEV4ZXJjaXNlIApDb2xvdXIgdGhlIE1EUyBwbG90IHdpdGggZGlmZmVyZW50IG1ldGFkYXRhIHZhcmlhYmxlcyBlLmcuIGltbXVub3BoZW5vdHlwZS4gClRyeSB1c2luZyBgc2hhcGU9YCBpbnNpZGUgdGhlIGFlcygpLiBZb3UgY2FuIHVzZSA/Z2VvbV9wb2ludCB0byBjaGVjayB0aGUgaGVscCBwYWdlLgpEaXNjdXNzIHdoYXQgaXMgdGhlIGdyZWF0ZXN0IHNvdXJjZSBvZiB2YXJpYXRpb24gaW4gdGhlIGRhdGEgKGkuZS4gd2hhdCBkb2VzIGRpbWVuc2lvbiAxIHJlcHJlc2VudCk/IFdoYXQgaXMgdGhlIHNlY29uZCBncmVhdGVzdCBzb3VyY2Ugb2YgdmFyaWF0aW9uIGluIHRoZSBkYXRhPwoKU29sdXRpb24KCmBgYHtyfQojIE1EUyBwbG90IGNvbG91cmVkIGJ5IGNlbGwgdHlwZQpnZ3Bsb3QoTURTZGltcywgYWVzKHg9RGltMSwgeT1EaW0yLCBjb2xvdXI9Y29uZGl0aW9uKSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0KIyBNRFMgcGxvdCBjb2xvdXJlZCBieSBjZWxsIHR5cGUKZ2dwbG90KE1EU2RpbXMsIGFlcyh4PURpbTEsIHk9RGltMiwgY29sb3VyPWltbXVub3BoZW5vdHlwZSkpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CiMgTURTIHBsb3QgY29sb3VyZWQgYnkgc3RhZ2UKZ2dwbG90KE1EU2RpbXMsIGFlcyh4PURpbTEsIHk9RGltMiwgY29sb3VyPWBkZXZlbG9wbWVudGFsIHN0YWdlYCkpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CiMgTURTIHBsb3QgY29sb3VyZWQgYnkgc3RhZ2Ugd2l0aCBzaGFwZSBmb3IgY2VsbCB0eXBlCmdncGxvdChNRFNkaW1zLCBhZXMoeD1EaW0xLCB5PURpbTIsIGNvbG91cj1gZGV2ZWxvcG1lbnRhbCBzdGFnZWAsIHNoYXBlPWltbXVub3BoZW5vdHlwZSkpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAoKRGVtbyBtb3JlIE1EUyBwbG90cyAoc2FtcGxlIHN3YXAsIGJhdGNoIGVmZmVjdHMpCgojIERpZmZlcmVudGlhbCBleHByZXNzaW9uCgpOb3cgdGhhdCB3ZSBhcmUgaGFwcHkgdGhhdCB0aGUgZGF0YSBsb29rcyBnb29kLCB3ZSBjYW4gY29udGludWUgdG8gdGVzdGluZyBmb3IgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLiBXZSB3aWxsIHVzZSB0aGUgYHRlc3RfZGlmZmVyZW50aWFsX2FidW5kYW5jZSgpYCBmcm9tIHR0QnVsayB3aGljaCB1c2VzIGVkZ2VSIHRvIHBlcmZvcm0gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLiBXZSBnaXZlIGB0ZXN0X2RpZmZlcmVudGlhbF9hYnVuZGFuY2UoKWAgb3VyIHR0QnVsayBjb3VudHMgb2JqZWN0LCBvdXIgZmlsdGVyaW5nIHRocmVzaG9sZHMgYW5kIGEgZm9ybXVsYSAoZS5nLiBgMCArIGNvbmRpdGlvbmApLCAKc3BlY2lmeWluZyB0aGUgY29sdW1uIHRoYXQgY29udGFpbnMgb3VyIGdyb3VwcyB0byBiZSBjb21wYXJlZC4gV2UgY2FuIGFsc28gcHJvdmlkZSB0aGUgbmFtZXMgb2YgdGhlIGdyb3VwcyB3ZSB3YW50IHRvIGNvbXBhcmUvY29udHJhc3QgdG8gLmNvbnRyYXN0cyAoZS5nLiAuY29udHJhc3RzID0gYygiY29uZGl0aW9uYnByZWcgLSBjb25kaXRpb25ibGFjdCIpKQpgYGB7cn0KIyBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB3aXRoIGxpbW1hLXZvb20gKG9yIGVkZ2VSKQpjb3VudHMuZGUgPC0gY291bnRzICU+JQogICAgdGVzdF9kaWZmZXJlbnRpYWxfYWJ1bmRhbmNlKAogICAgICAuZm9ybXVsYSA9IH4gMCArIGNvbmRpdGlvbiwKICAgICAgLmNvbnRyYXN0cyA9IGMoImNvbmRpdGlvbmJwcmVnIC0gY29uZGl0aW9uYmxhY3QiKSwKICAgICAgY3BtX3RocmVzaG9sZCA9IDAuNSwKICAgICAgcHJvcF90aHJlc2hvbGQgPSAyLzEyKQojIHRha2UgYSBsb29rCmNvdW50cy5kZQpgYGAKTm93IHdlIGhhdmUgY29sdW1ucyB3aXRoIG91ciBsb2dGQyBhbmQgRkRSIFAgdmFsdWVzLgoKIyMjIyBFeGVyY2lzZQpQZXJmb3JtIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGZvciBscHJlZyB2cyBsbGFjdAoKUGVyZm9ybSBmb3IgMiBjb250cmFzdHMsIGJwcmVnIHZzIGJsYWN0IGFuZCBscHJlZyB2cyBsbGFjdCBhdCB0aGUgc2FtZSB0aW1lCgpgYGB7cn0KIyBtYXliZSBpbmNsdWRlCiMgcmVtb3ZlIGNvbmRpdGlvbiBmcm9tIGNvbHVtbiBuYW1lcwojIGNvdW50cy5kZSA8LSBjb3VudHMuZGUgJT4lIAojICByZW5hbWVfYXQodmFycyhjb250YWlucygiY29uZGl0aW9uIikpLCB+IHN0cl9yZXBsYWNlKC4sICJfY29uZGl0aW9uIiwgIiIpKQpgYGAKCldlIGNhbiB0YWtlIGEgbG9vayBhdCB0aGUgdG9wIGdlbmVzIGJ5IFAgdmFsdWUuIFdlIGNhbiB1c2UgYGZpbHRlcigpYCB0byBzZWxlY3Qgb25seSBnZW5lcyB3aXRoIEZEUiA8IDAuMDUsIHRoZW4gdXNlIGBkaXN0aW5jdCgpYCB0byBnZXQgdGhlIHVuaXF1ZSB2YWx1ZXMgZm9yIGNvbHVtbnMgb2YgaW50ZXJlc3QsIGFuZCBgYXJyYW5nZSgpYCB0byBzb3J0IHRoZSB0YWJsZSBieSBQVmFsdWUuCgpgYGB7ciBldmFsPUZBTFNFfQpjb3VudHMuZGUgJT4lCiAgZmlsdGVyKEZEUiA8IDAuMDUpICU+JQogIGRpc3RpbmN0KGdlbmVfc3ltYm9sLCBsb2dGQywgUFZhbHVlLCBGRFIpICU+JQogIGFycmFuZ2UoUFZhbHVlKQpgYGAKCldlIGNhbiB3cml0ZSBvdXQgb3VyIHJlc3VsdHMgdG8gYSBmaWxlIHRoYXQgY2FuIGJlIGxvYWRlZCBpbnRvIGUuZy4gRXhjZWwuIGB3cml0ZV90c3YoKWAgd2lsbCBjcmVhdGUgYSB0YWItc2VwYXJhdGVkIGZpbGUuCgpgYGB7ciBldmFsPUZBTFNFfQojIHNhdmUgcmVzdWx0cwp3cml0ZV90c3YoY291bnRzLmRlLCAibXlfZGVfcmVzdWx0cy50c3YiKQpgYGAKCiMgUGxvdHMgYWZ0ZXIgdGVzdGluZyBmb3IgREUKCkxldCdzIG1ha2UgYSBmZXcgcGxvdHMgdG8gbWFrZSBzdXJlIGV2ZXJ5dGhpbmcgbG9va3MgZ29vZCBhbmQgdGhhdCB3ZSBoYXZlbid0IG1hZGUgYSBtaXN0YWtlIGluIHRoZSBhbmFseXNpcy4gR2Vub21lLXdpZGUgcGxvdHMgdGhhdCBhcmUgdXNlZnVsIGZvciBjaGVja2luZyBhcmUgTUEgcGxvdHMgYW5kIHZvbGNhbm8gcGxvdHMuIFdlIGNhbiBhbHNvIHVzZSBzdHJpcGNoYXJ0cyBhbmQgaGVhdG1hcHMgdG8gdmlzdWFsaXNlIGdyb3VwcyBvZiBnZW5lcy4KCiMjIyBNQSBwbG90cwoKTUEgcGxvdHMgZW5hYmxlIHVzIHRvIHZpc3VhbGlzZSAqKmFtb3VudCoqIG9mIGV4cHJlc3Npb24gKGxvZ0NQTSkgdmVyc3VzIGxvZ0ZDLiBIaWdobHkgZXhwcmVzc2VkIGdlbmVzIGFyZSB0b3dhcmRzIHRoZSByaWdodCBvZiB0aGUgcGxvdC4gV2UgY2FuIGFsc28gY29sb3VyIHNpZ25pZmljYW50IGdlbmVzIChlLmcuIGdlbmVzIHdpdGggRkRSIDwgMC4wNSkgCgpgYGB7cn0KIyBtYXBsb3QsIG1pbmltYWwKY291bnRzLmRlICU+JQogIGZpbHRlcihgZmlsdGVyIG91dCBsb3cgY291bnRzYCA9PSBGQUxTRSkgJT4lCiAgZ2dwbG90KGFlcyh4PWxvZ0NQTSwgeT0tbG9nRkMsIGNvbG91cj1pc19kZSkpICsKICBnZW9tX3BvaW50KCkKYGBgCiMjIyBWb2xjYW5vIHBsb3RzCgpWb2xjYW5vIHBsb3RzIGVuYWJsZSB1cyB0byB2aXN1YWxpc2UgKipzaWduaWZpY2FuY2UqKiBvZiBleHByZXNzaW9uIChsb2dDUE0pIHZlcnN1cyBsb2dGQy4gSGlnaGx5IHNpZ25pZmljYW50IGdlbmVzIGFyZSB0b3dhcmRzIHRoZSB0b3Agb2YgdGhlIHBsb3QuIFdlIGNhbiBhbHNvIGNvbG91ciBzaWduaWZpY2FudCBnZW5lcyAoZS5nLiBnZW5lcyB3aXRoIEZEUiA8IDAuMDUpIAoKYGBge3J9CiMgdm9sY2Fub3Bsb3QsIG1pbmltYWwKY291bnRzLmRlICU+JQogIGZpbHRlcihgZmlsdGVyIG91dCBsb3cgY291bnRzYCA9PSBGQUxTRSkgJT4lCiAgZ2dwbG90KGFlcyh4PWxvZ0ZDLCB5PS1sb2cxMChQVmFsdWUpLCBjb2xvdXI9aXNfZGUpKSArCiAgZ2VvbV9wb2ludCgpCmBgYApUbyBzZWUgaG93IHRvIG1ha2UgbW9yZSBjb21wbGljYXRlZCB2b2xjYW5vIHBsb3RzLCBpbmNsdWRpbmcgaG93IHRvIGxhYmVsIGdlbmVzIGluIHRoZSBwbG90LCBzZWUgdGhlIHZvbGNhbm8gcGxvdCB0dXRvcmlhbCBbaGVyZV0oaHR0cHM6Ly9wbWFjZGFzY2kuZ2l0aHViLmlvL3ItaW50cm8tdGlkeXZlcnNlL3ZvbGNhbm9wbG90Lmh0bWwpLiBNb3JlIGNvbXBsaWNhdGVkIE1BIHBsb3RzIGNvdWxkIGFsc28gYmUgbWFkZSBpbiBhIHNpbWlsYXIgd2F5LgoKIyMjIFN0cmlwY2hhcnRzCgpJbiBhZGRpdGlvbiB0byB0aGUgZ2Vub21lLXdpZGUgcGxvdHMgYWxyZWFkeSBkaXNjdXNzZWQsIGl0IGlzIHJlY29tbWVuZGVkIHRvIGhhdmUgYSBsb29rIGF0IHRoZSBleHByZXNzaW9uIGxldmVscyBvZiB0aGUgaW5kaXZpZHVhbCBzYW1wbGVzIGZvciB0aGUgZ2VuZXMgb2YgaW50ZXJlc3QsIGJlZm9yZSBmb2xsb3dpbmcgdXAgb24gdGhlIERFIGdlbmVzIHdpdGggZnVydGhlciBsYWIgd29yay4gV2UgY2FuIHVzZSBzdHJpcGNoYXJ0cyBhbmQgaGVhdG1hcHMgdG8gZG8gdGhpcy4gVGhlc2Ugd2lsbCBoZWxwIHNob3cgaWYgZXhwcmVzc2lvbiBpcyBjb25zaXN0ZW50IGFtb25nc3QgcmVwbGljYXRlcyBpbiB0aGUgZ3JvdXBzLgoKRm9yIHRoZXNlIHBsb3RzIHNob3dpbmcgZXhwcmVzc2lvbiBpbiB0aGUgaW5kaXZpZHVhbCBzYW1wbGVzLCB3ZSBuZWVkIHRoZSBub3JtYWxpc2VkIGNvdW50cyBwZXIgZ2VuZSBwZXIgc2FtcGxlLCBzbyB3ZSB3aWxsIGZpcnN0IGpvaW4gdGhlIHNjYWxlZCBjb3VudHMgdG8gdGhlIGRlIHJlc3VsdHMuCmBgYHtyfQpjb3VudHMuZGUgPC0gZnVsbF9qb2luKGNvdW50cy5kZSwgY291bnRzLm5vcm0pCmBgYApXaXRoIHN0cmlwY2hhcnRzIHdlIGNhbiBzZWUgaWYgcmVwbGljYXRlcyB0ZW5kIHRvIGdyb3VwIHRvZ2V0aGVyIGFuZCBob3cgdGhlIGV4cHJlc3Npb24gY29tcGFyZXMgdG8gdGhlIG90aGVyIGdyb3Vwcy4gCipOb3RlIHRoaXMgaXMgYml0IG1vcmUgY29tcGxpY2F0ZWQgbWF5YmUgb21pdC4gT3IgYnJlYWsgaXQgaW50byBzdGVwcyBlLmcuIGdldCB0b3AgZ2VuZXMsIHRoZW4gcGxvdCoKCmBgYHtyfQojIHN0cmlwY2hhcnRzCmNvdW50cy5kZSAlPiUKCWlubmVyX2pvaW4oICguKSAlPiUgZGlzdGluY3QoZ2VuZV9zeW1ib2wsIFBWYWx1ZSkgJT4lIGFycmFuZ2UoUFZhbHVlKSAlPiUgaGVhZCg2KSkgJT4lCglnZ3Bsb3QoYWVzKHggPSBjb25kaXRpb24sIHkgPSBsb2cyKGBjb3VudCBzY2FsZWRgICsgMSksIGNvbG91ciA9IGNvbmRpdGlvbikpICsKCWdlb21faml0dGVyKCkgKwoJZmFjZXRfd3JhcCh+Z2VuZV9zeW1ib2wpCmBgYAoKIyMjIEhlYXRtYXBzCgpXZSBjYW4gY3JlYXRlIGhlYXRtYXBzIGZvciB0aGUgbW9zdCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIEZvciBleGFtcGxlIHdlIGNvdWxkIHNlbGVjdCB0aGUgZ2VuZXMgd2l0aCBGRFIgPCAwLjA1IGFuZCBhIGxvZ0ZDIGNoYW5nZSBvZiA0IGFuZCBtYWtlIGEgaGVhdG1hcCBvZiB0aG9zZS4KCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTh9CiMgYmFzaWMKY291bnRzLmRlICU+JQogIGZpbHRlcihGRFIgPCAwLjA1ICYgYWJzKGxvZ0ZDKSA+IDQpICU+JQogIGhlYXRtYXAoCiAgICAgICAgLmhvcml6b250YWwgPSBzYW1wbGUsCiAgICAgICAgLnZlcnRpY2FsID0gZ2VuZV9zeW1ib2wsCiAgICAgICAgLmFidW5kYW5jZSA9IGBjb3VudCBzY2FsZWRgLAogICAgICAgIGFubm90YXRpb24gPSBjKGltbXVub3BoZW5vdHlwZSwgYGRldmVsb3BtZW50YWwgc3RhZ2VgKSwKICAgICAgICBsb2dfdHJhbnNmb3JtID0gVFJVRQogICAgKQpgYGAKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OH0KIyBjdXN0b21pc2VkCmNvdW50cy5kZSAlPiUKICBmaWx0ZXIoRkRSIDwgMC4wNSAmIGFicyhsb2dGQykgPiA0KSAlPiUKICBoZWF0bWFwKAogICAgICAgIC5ob3Jpem9udGFsID0gc2FtcGxlLAogICAgICAgIC52ZXJ0aWNhbCA9IGdlbmVfc3ltYm9sLAogICAgICAgIC5hYnVuZGFuY2UgPSBgY291bnQgc2NhbGVkYCwKICAgICAgICBhbm5vdGF0aW9uID0gYyhpbW11bm9waGVub3R5cGUsIGBkZXZlbG9wbWVudGFsIHN0YWdlYCksCiAgICAgICAgbG9nX3RyYW5zZm9ybSA9IFRSVUUsCiAgICAgICAgcGFsZXR0ZV9hYnVuZGFuY2UgPSBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpLAogICAgICAgIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSA4KQogICAgKQpgYGAKCiMgS2V5IFBvaW50cwotIFJOQS1TZXEgZGF0YSBjYW4gYmUgYW5hbHlzZWQgaW4gYSAndGlkeScgd2F5IHVzaW5nIHRoZSBwYWNrYWdlcyB0aWR5dmVyc2UsIHR0QnVsayBhbmQgdGlkeUhlYXRtYXAKLSBLZXkgc3RlcHMgaW4gYW4gUk5BLVNlcSBhbmFseXNpcyBhcmUgZmlsdGVyaW5nIGxvd2x5IGV4cHJlc3NlZCBnZW5lcywgbm9ybWFsaXNhdGlvbiBmb3Igc2VxdWVuY2luZyBkZXB0aCBhbmQgY29tcG9zaXRpb24sIGFuZCB0ZXN0aW5nIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgotIE1EUyBwbG90cyBhcmUgdmVyeSBpbXBvcnRhbnQgZm9yIGV4YW1pbmluZyB0aGUgcXVhbGl0eSBvZiB0aGUgZGF0YQotIE90aGVyIHVzZWZ1bCBwbG90cyBmb3IgYXNzZXNzaW5nIFJOQS1TZXEgZGF0YSBhcmUgYmFyIHBsb3RzLCBkZW5zaXR5IHBsb3RzLCBib3ggcGxvdHMsIE1BIHBsb3RzLCB2b2xjYW5vIHBsb3RzLCBzdHJpcGNoYXJ0cyBhbmQgaGVhdG1hcHMKCgojIEZ1cnRoZXIgUmVhZGluZwpbUk5BLVNlcSBhbmFseXNpcyBpcyBlYXN5IGFzIDEtMi0zIHdpdGggbGltbWEsIEdsaW1tYSBhbmQgZWRnZVJdKGh0dHBzOi8vZjEwMDByZXNlYXJjaC5jb20vYXJ0aWNsZXMvNS0xNDA4KSAKW1JOQS1TZXEgYW5hbHlzaXMgaW4gUl0oaHR0cDovL2NvbWJpbmUtYXVzdHJhbGlhLmdpdGh1Yi5pby9STkFzZXEtUi8wNi1ybmFzZXEtZGF5MS5odG1sKQoK